`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    02:05:52 04/02/2011 
// Design Name: 
// Module Name:    multiOperandAdder 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//operands is arranged with operands in sequence and will be added
//(when using the simple inverted stairs) from low positions to high positions
//though it really shouldn't matter

//////////////////////////////////////////////////////////////////////////////////
module multiOperandAdder #(parameter NM=16, K=4)(
    input [((NM)*K)-1:0] operands,
    output [NM-1:0] s_out,
	 input clock
    );

//use this if it is the BrentKung adder
//currently configured to handle up to 128 bits
/*	parameter DELAY = ((NM) <= 2) ? 1 : 
		((NM) <= 4) ? 2 : 
		((NM) <= 8) ? 3 : 
		((NM) <= 16) ? 4 : 
		((NM) <= 32) ? 5 : 
		((NM) <= 64) ? 6 : 7;
*/	
	//use this if it is the Ripple carry adder
	parameter DELAY = NM-1;
	
	//registers to store the data as it propogates forward
	//when addressing it will be [delay][row][column][bit]
	//where row indicates which add block we are parallel to
	//and column indicates which operand we are moving (first one not used in the first sum block starts at 1)
	reg [NM-1:0] stores [0:DELAY-1][1:((K-2)*(K-2))];
	//reg [NM-1:0] stores [0:DELAY-1][1:K-2][1:K-2];
	//wire for the outputs
	//so it can be assigned to the registers at the appropriate time
	wire [NM-1:0] add_out [K-3:0];//dont need to store the final adder output - just send it out
	
	genvar i,j,a;
	
	NbitPipelinedAdder #(.N(NM)) adder0 (
		.x_in(operands[NM-1:0]),
		.y_in(operands[((2*NM)-1):NM]),
		.c_in(1'b0), 
		.s_out(add_out[0]),
		.clock(clock));
	generate
		//instantiate the adders
		//this is where the change between adder topologies would occur
		for(i=1;i<K-1;i=i+1) begin:adderloop
			if(i==(K-2)) begin
				NbitPipelinedAdder #(.N(NM)) adder(
				  .x_in(add_out[i-1]),
				  .y_in(stores[DELAY-1][(i-1)*(K-2)+i]),
				  //.y_in(stores[DELAY-1][i][i]),
				  .c_in(1'b0),
				  .s_out(s_out),
				  .clock(clock));
			end else begin
				NbitPipelinedAdder #(.N(NM)) adder(
				  .x_in(add_out[i-1]),
				  .y_in(stores[DELAY-1][(i-1)*(K-2)+i]),
				  //.y_in(stores[DELAY-1][i][i]),
				  .c_in(1'b0),
				  .s_out(add_out[i]),
				  .clock(clock));
			end
		end
		
		for(i=1; i<(K-1); i=i+1) begin:regShiftOperand
			for(j=1; j<(K-1); j=j+1)begin:regShiftAdderLevel
				for(a=0; a<DELAY; a=a+1)begin:regShiftDelay
					
					always @(posedge clock) begin
						if(a==0) begin
							if(j==1) begin
								stores[a][(j-1)*(K-2)+i] <= operands[((i+2)*NM)-1:((i+1)*NM)];
								//stores[a][j][i] <= operands[((i+2)*NM)-1:((i+1)*NM)];
							end else begin//never in this loop when j is one
								stores[a][(j-1)*(K-2)+i] <= stores[DELAY-1][(j-2)*(K-2)+i];
								//stores[a][j][i] <= stores[DELAY-1][j-1][i];
							end
						end else begin
							stores[a][(j-1)*(K-2)+i] <= stores[a-1][(j-1)*(K-2)+i];
							//stores[a][j][i] <= stores[a-1][j][i];
						end
					end //always
								
				end//for regShiftDelay
			end
		end
		
	endgenerate
			

endmodule
